<!-- directive:title lixp,liuyp::防抖节流整理 -->
<!-- directive:breadcrumb 防抖节流整理 -->
<div class="panel-body">
    <style type="text/css">
        .debounceTestDiv{background: #fad733; height: 40px; line-height: 40px;}
        .debounceTestDiv b{display: inline-block; color: #fff;width: 50px;border-left:1px solid #9933cc;}
    </style>
    <button type="button" class="btn btn-danger" ng-click="test()">开始测试</button>
    <hr />
    <b>防抖动</b>
    <hr />
    <p>实验目的 测试防抖节流的延迟头尾效果以及记录对应的应用场景</p>
    <p>1 设置一个div#debounceTest</p>
    <p>2 每400ms点击一次 设置最大等待时间3s 延迟时间500ms 延迟尾(或者叫 超时的后沿调用)</p>
    <div id="debounceTest1">
        <div class="debounceTestDiv"></div>
    </div>
    <p>3 前八次 每点击一次就会延迟500ms执行 到达最大时间3s"不得不"执行</p>
    <p>4 后七次 点击进入新一轮防抖 最后一次结束后500ms妥妥执行</p>
    <hr />
    <p class="text-danger">可以应用在输入框实时搜索</p>
    <input type="text" ng-model="doSearchOnRealTimeContent" ng-change="doSearchOnRealTime()" />
    {{ doSearchOnRealTimeFlag }}
    <hr />
    <p>1 设置一个div#debounceTest</p>
    <p>2 每400ms点击一次 设置最大等待时间3s 延迟时间500ms 延迟头(或者叫 超时的前沿调用)</p>
    <div id="debounceTest2">
        <div class="debounceTestDiv"></div>
    </div>
    <p>3 点击第一次即触发任务 每点击一次延时500ms禁令 直到最大等待时间解开禁令</p>
    <p>4 禁令解开后点击一次触发 再点击再延时</p>
    <p>5 注意 如果不设置最大时间 他会一直一直一直禁令下去</p>
    <hr />
    <p>1 设置一个div#debounceTest</p>
    <p>2 每400ms点击一次 设置最大等待时间3s 延迟时间500ms 延迟尾延迟头全开</p>
    <div id="debounceTest3">
        <div class="debounceTestDiv"></div>
    </div>
    <p>3 点击第一次即触发任务 然后等待最大时间 然后解开禁令重新可点击</p>
    <p>4 似乎没啥用</p>
    <hr />
    <b>节流阀</b>
    <hr />
    <p>1 设置一个div#debounceTest</p>
    <p>2 每400ms点击一次 延迟时间3s 延迟尾延迟头全开</p>
    <div id="debounceTest4">
        <div class="debounceTestDiv"></div>
    </div>
    <p>3 第一次执行 禁令期间禁止点击 放开即可点击</p>
    <hr />
    <button type="button" ng-click="doSubmitOnSomeTime()" class="btn btn-danger">{{ doSubmitOnSomeTimeFlag || '测试' }}</button>
    <hr />
    <p>1 设置一个div#debounceTest</p>
    <p>2 每400ms点击一次 延迟时间3s 延迟尾开</p>
    <div id="debounceTest5">
        <div class="debounceTestDiv"></div>
    </div>
    <p>3 点击第一下禁令 等待3执行逻辑 之后进入下一轮</p>
    <p class="text-danger">可以应用在提交按钮多次点击</p>
    <hr />
    <p>1 设置一个div#debounceTest</p>
    <p>2 每400ms点击一次 延迟时间3s 延迟头开</p>
    <div id="debounceTest6">
        <div class="debounceTestDiv"></div>
    </div>
    <p>3 第一次执行 禁令期间禁止点击 3s后放开即可点击</p>
    <script type="text/js" ui-bs>
        function debounce(func, wait, options) {
            var lastArgs,
                lastThis,
                maxWait,
                result,
                timerId,
                lastCallTime,
                lastInvokeTime = 0,
                leading = false,
                maxing = false,
                trailing = true;

            if (typeof func != 'function') {
                throw new TypeError('报错');
            }
            wait = _.toNumber(wait) || 0;
            if (_.isObject(options)) {
                leading = !!options.leading;
                maxing = 'maxWait' in options;
                maxWait = maxing ? Math.max(_.toNumber(options.maxWait) || 0, wait) : maxWait;
                trailing = 'trailing' in options ? !!options.trailing : trailing;
            }

            function invokefunc(time) {
                var args = lastArgs,
                    thisArg = lastThis;

                lastArgs = lastThis = undefined;
                lastInvokeTime = time;
                result = func.apply(thisArg, args);
                return result;
            }

            function leadingEdge(time) {
                // Reset any `maxWait` timer.
                lastInvokeTime = time;
                // Start the timer for the trailing edge.
                timerId = setTimeout(timerExpired, wait);
                // Invoke the leading edge.
                return leading ? invokefunc(time) : result;
            }

            function remainingwait(time) {
                var timeSinceLastCall = time - lastCallTime,
                    timeSinceLastInvoke = time - lastInvokeTime,
                    timewaiting = wait - timeSinceLastCall;

                return maxing
                    ? Math.min(timewaiting, maxWait - timeSinceLastInvoke)
                    : timewaiting;
            }

            function shouldInvoke(time) {
                var timeSinceLastCall = time - lastCallTime,
                    timeSinceLastInvoke = time - lastInvokeTime;

                // Either this is the first call, activity has stopped and we're at the
                // trailing edge, the system time has gone backwards and we're treating
                // it as the trailing edge, or we've hit the `maxWait` limit.
                return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
                    (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
            }

            function timerExpired() {
                var time = _.now();
                if (shouldInvoke(time)) {
                    return trailingEdge(time);
                }
                // Restart the timer.
                timerId = setTimeout(timerExpired, remainingwait(time));
            }

            function trailingEdge(time) {
                timerId = undefined;

                // Only invoke if we have `lastArgs` which means `func` has been
                // debounced at least once.
                if (trailing && lastArgs) {
                    return invokefunc(time);
                }
                lastArgs = lastThis = undefined;
                return result;
            }

            function cancel() {
                if (timerId !== undefined) {
                    clearTimeout(timerId);
                }
                lastInvokeTime = 0;
                lastArgs = lastCallTime = lastThis = timerId = undefined;
            }

            function flush() {
                return timerId === undefined ? result : trailingEdge(_.now());
            }

            function debounced() {
                var time = _.now(),
                    isInvoking = shouldInvoke(time);

                lastArgs = arguments;
                lastThis = this;
                lastCallTime = time;

                if (isInvoking) {
                    if (timerId === undefined) {
                        return leadingEdge(lastCallTime);
                    }
                    if (maxing) {
                        // Handle invocations in a tight loop.
                        timerId = setTimeout(timerExpired, wait);
                        return invokefunc(lastCallTime);
                    }
                }
                if (timerId === undefined) {
                    timerId = setTimeout(timerExpired, wait);
                }
                return result;
            }
            debounced.cancel = cancel;
            debounced.flush = flush;
            return debounced;
        }
        function throttle(func, wait, options) {
            var leading = true,
                trailing = true;
            if (typeof func != 'function') {
                throw new TypeError('出错了');
            }
            if (_.isObject(options)) {
                leading = 'leading' in options ? !!options.leading : leading;
                trailing = 'trailing' in options ? !!options.trailing : trailing;
            }
            return debounceFromLodashForTest(func, wait, {
                'leading': leading,
                'maxWait': wait,
                'trailing': trailing
            });
        }
    </script>
</div>
